import base64
import binascii
import logging
import threading

from PyQt5.QtBluetooth import QLowEnergyCharacteristic, QLowEnergyService, QLowEnergyDescriptor, QBluetoothUuid
from PyQt5.QtCore import QObject, QEvent, pyqtSignal, QByteArray
from PyQt5.QtWidgets import QWidget

from widget.characteristic_node_widget_ui import Ui_CharacteristicNodeWidget
from widget.descriptor_node_widget import DescriptorNodeWidget
from widget.node_content_widget import NodeContentWidget


class CharacteristicNodeWidget(QWidget, Ui_CharacteristicNodeWidget):
    # write_requested = pyqtSignal(int)
    # read_requested = pyqtSignal(int)

    def __init__(self, service: QLowEnergyService, characteristic: QLowEnergyCharacteristic, parent: NodeContentWidget,
                 index, *args, **kwargs):
        super(CharacteristicNodeWidget, self).__init__(*args, **kwargs)
        self.setupUi(self)
        self.ENABLE_NOTIFICATION_VALUE = QByteArray.fromHex(b"0100")
        self.DISABLE_NOTIFICATION_VALUE = QByteArray.fromHex(b"0000")
        self._service = service
        self._characteristic = None
        self.characteristic = characteristic
        self._parent: NodeContentWidget = parent
        self.descriptor_sub_widget = NodeContentWidget(self._parent)
        self.descriptor_sub_widget.setObjectName("DescriptorSubWidget")
        self.descriptor_sub_widget.title_name = "{}".format(characteristic.name())
        self._parent.insert_item_to_child_area(self.descriptor_sub_widget)
        self.descriptor_widget_list = []
        self._index = index
        self._custom_ui()
        self._signal_init()

    def _custom_ui(self):
        self.installEventFilter(self)
        self.characteristic_read_pb.hide()
        self.characteristic_write_pb.hide()
        self.update_display_information()

    def _signal_init(self):
        self.characteristic_write_pb.clicked.connect(self.on_characteristic_write_button_clicked)
        self.characteristic_read_pb.clicked.connect(self.on_characteristic_read_button_clicked)
        self.notify_enable_pb.clicked.connect(self.on_characteristic_ctl_button_clicked)

    @property
    def characteristic(self):
        return self._characteristic

    @characteristic.setter
    def characteristic(self, characteristic: QLowEnergyCharacteristic):
        self._characteristic = characteristic

        # test
        print("characteristic")
        c = characteristic
        """
        https://www.cnblogs.com/zuiniub/p/13229495.html
        特别的描述符值得特别地提起：客户端特性配置描述符(Client Characteristic Configuration Descriptor，CCCD)，
        它的UUID是0x2902，这个描述符是给任何支持通知或指示功能的特性额外增加的。在CCCD中写入“1”使能通知功能，写入“2”使能指示功能，
        写入“0”同时禁止通知和指示功能。如果属性值的许可（permissions）含有通知或通知时，特性中的属性中需要有客户端特性配置描述符（CCCD）条目
        """
        for descriptor in c.descriptors():
            print(descriptor.name())
            if descriptor.uuid().toUInt16()[0] == 0x2092:  ## CCCD
                self._service.writeDescriptor(self.descriptor, self.ENABLE_NOTIFICATION_VALUE)
        print(c.name())
        print(hex(c.uuid().toUInt16()[0]))
        if c.isValid():
            if c.properties() & QLowEnergyCharacteristic.WriteNoResponse or c.properties() & QLowEnergyCharacteristic.Write:
                # self.m_writeCharacteristic = c
                if c.properties() & QLowEnergyCharacteristic.WriteNoResponse:
                    self.m_writemode = QLowEnergyService.WriteWithoutResponse
                else:
                    self.m_writemode = QLowEnergyService.WriteWithResponse
            self.m_notificationDesc = c.descriptor(
                QBluetoothUuid(QBluetoothUuid.ClientCharacteristicConfiguration))
            if self.m_notificationDesc.isValid():
                print("m_notificationDesc.isValid")
                # self.notify_enable_pb.hide()  # 不支持notify
                # self._service.writeDescriptor(self.m_notificationDesc, self.ENABLE_NOTIFICATION_VALUE)
            else:
                print("m_notificationDesc.is not Valid")
                self.notify_enable_pb.setText("Enable Notify")

    def on_characteristic_write_button_clicked(self):
        """
        TODO 不要在servic_node_widget中处理,去掉信号量  直接在这个位置处理按键事件
        :return:
        """
        print("on_characteristic_write_button_clicked")
        try:
            if self.characteristic is not None:
                # QByteArray("kbr".encode()) 下位机可以把kb解析出来
                # descriptors = characteristic.descriptors()
                # self.service.writeDescriptor(descriptors[0], QByteArray("0100".encode()))
                self._service.writeCharacteristic(self.characteristic, QByteArray("kbr".encode()),
                                                  QLowEnergyService.WriteWithResponse)

        except Exception as e:
            print(e)

    def on_characteristic_read_button_clicked(self):
        read_thread = threading.Thread(target=self.read_characteristic_entry)
        read_thread.start()

    def read_characteristic_entry(self):
        try:
            print("read_characteristic_entry")
            print("characteristic" + str(self.characteristic.value()))
            if self.characteristic is not None:
                self._service.readCharacteristic(self.characteristic)
        except Exception as e:
            print(e)

    def on_characteristic_ctl_button_clicked(self):
        if self.notify_enable_pb.text() == "Enable Notify":
            self.notify_enable_pb.setText("Disable Notify")
            self.on_characteristic_enable_notify_button_clicked()
        else:
            self.notify_enable_pb.setText("Enable Notify")
            self.on_characteristic_disable_notify_button_clicked()

    def on_characteristic_enable_notify_button_clicked(self):
        try:
            print(self.characteristic.uuid().toString())
            notification: QLowEnergyDescriptor = self.characteristic.descriptor(
                QBluetoothUuid(QBluetoothUuid.ClientCharacteristicConfiguration))
            # self._service.writeDescriptor(notification, bytes.fromhex('0100'))
            self._service.writeDescriptor(self.m_notificationDesc, self.ENABLE_NOTIFICATION_VALUE)
            if notification.isValid():
                print("is valid")
                # self._service.writeDescriptor(notification, QByteArray(b'\x01\x00'))
            else:
                print("is not valid")
        except Exception as e:
            print(e)

    def on_characteristic_disable_notify_button_clicked(self):
        try:
            print(self.characteristic.uuid().toString())
            notification: QLowEnergyDescriptor = self.characteristic.descriptor(
                QBluetoothUuid(QBluetoothUuid.ClientCharacteristicConfiguration))
            # self._service.writeDescriptor(notification, bytes.fromhex('0100'))
            self._service.writeDescriptor(notification, QByteArray(b'\x00\x00'))
            if notification.isValid():
                print("is valid")
                self._service.writeDescriptor(notification, QByteArray(b'\x00\x00'))
            else:
                print("is not valid")
        except Exception as e:
            print(e)

    def update_display_information(self):
        if self._characteristic.name() == "":
            self.characteristic_name_lb.setText("Name is NULL")
        else:
            self.characteristic_name_lb.setText(self._characteristic.name())
        self.uuid_value_lb.setText(self._characteristic.uuid().toString())
        # 属性
        p = self._characteristic.properties()
        p_str = ""
        if p and self._characteristic.Unknown:
            p_str += "Unknown; "
        if p and self._characteristic.Broadcasting:
            p_str += "Broadcasting; "
        if p and self._characteristic.Read:
            p_str += "Read; "
            self.characteristic_read_pb.show()
        if p and self._characteristic.WriteNoResponse:
            p_str += "WriteNoResponse; "
        if p and self._characteristic.Write:
            p_str += "Write; "
            self.characteristic_write_pb.show()
        if p and self._characteristic.Notify:
            p_str += "Notify; "
        if p and self._characteristic.Indicate:
            p_str += "Indicate; "
        if p and self._characteristic.WriteSigned:
            p_str += "WriteSigned; "
        if p and self._characteristic.ExtendedProperty:
            p_str += "ExtendedProperty; "
        self.property_value_lb.setText(p_str)
        self.descriptor_num_lb.setText(str(len(self._characteristic.descriptors())))
        try:
            # FIXME: 字符编码方式有问题
            b_value = binascii.hexlify(self._characteristic.value())
            s_value = b_value.decode('utf-8')
            self.value_value_lb.setText(s_value)
        except Exception as e:
            logging.error(e)
        # 添加descriptor
        descriptors = self._characteristic.descriptors()
        if len(descriptors) == 0:
            logging.debug("no descriptors")
            return
        print("add descriptor")
        for descriptor in descriptors:
            self.add_descriptor_to_list(descriptor)

    def add_descriptor_to_list(self, descriptor):
        try:
            widget = DescriptorNodeWidget(self._service, descriptor, self.descriptor_sub_widget,
                                          len(self.descriptor_widget_list))
            self.descriptor_widget_list.append(widget)
            self.descriptor_sub_widget.insert_item_to_parent_area(widget)
        except Exception as e:
            print(e)

    def eventFilter(self, ob: QObject, event: QEvent) -> bool:
        if event.type() == QEvent.MouseButtonDblClick:
            print("double clicked：" + self._characteristic.name())
            if len(self._characteristic.descriptors()) != 0:
                self.descriptor_sub_widget.item_double_clicked()
        return super().eventFilter(ob, event)

    def set_value_text(self, value: bytearray):
        self.value_value_lb.setText(binascii.hexlify(value).decode('utf-8'))
